using System;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using OfficeOpenXml;
using System.Collections.Generic;
using Google.Protobuf;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.Linq;
using ProtoDefine;

namespace ProtoDefine
{
    [ExecuteInEditMode]
    public class ExcelSheetEditor : EditorWindow
    {
        private Dictionary<string, ProtoMessageInfo> MessageTable = new Dictionary<string, ProtoMessageInfo>();
        private Dictionary<string, string> TypeMapping = new Dictionary<string, string>();

        private static ExcelSheetEditor window;
        public static PathsInfo mToolInfo;

        private Vector2 m_ScrollPosition;

        //还需要完善的
        //添加对不同语言的分割

        [MenuItem("Tools/数据表生成工具", priority = 1)]
        public static void EditorMain()
        {
            window = (ExcelSheetEditor)EditorWindow.GetWindow<ExcelSheetEditor>(true);
            window.titleContent = new GUIContent("ConfigPart Tool");
            window.Show();
        }

        private void OnEnable()
        {
            LoadUserData();
        }

        private void OnDisable()
        {
            SaveUserData();
            MessageTable.Clear();
        }

        private void OnGUI()
        {
            if (window == null)
            {
                window = (ExcelSheetEditor)EditorWindow.GetWindow<ExcelSheetEditor>(true);
            }

            m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);

            #region ---Excel Setting---
            GUILayout.Space(20);
            EditorGUILayout.LabelField("---Excel Setting---");

            mToolInfo.KeyInExcelToParse = ShowTextField("KeyInExcelToParse :", mToolInfo.KeyInExcelToParse, "Excel中那个Sheet被用作数据表格");
            mToolInfo.NameLineInExcel = ShowIntField("NameLineInExcel :", mToolInfo.NameLineInExcel, "名称在第几行");
            mToolInfo.TypeLineInExcel = ShowIntField("TypeLineInExcel :", mToolInfo.TypeLineInExcel, "类型在第几行");
            mToolInfo.DescLineInExcel = ShowIntField("DescLineInExcel :", mToolInfo.DescLineInExcel, "描述在第几行,不需要填-1");
            mToolInfo.DataLineInExcel = ShowIntField("DataLineInExcel :", mToolInfo.DataLineInExcel, "数据在第几行开始");

            var color = GUI.skin.GetStyle("Label");
            Color oldcolor = color.normal.textColor;
            color.normal.textColor = Color.red;

            GUILayout.Label(
               "支持类型 :" +
                "\n--> 全小写：double; float; int; bool; string; bytes 以及区分大小写的表名" +
                "\n--> 每张表必须具有ID 字段" +
                "\n--> 先生成脚本，再生成数据，不改结构才可以直接生成数据 " +
                "\n--> 运行时环境是4.x" +
                "\n--> 数组类型格式: repeat_type 例: repeat_int 用的时候 值使用“;”隔开" +
                "\n--> 嵌套类型格式: repeat_type 例: repeat_AchievementConfig(表名,必须完全一样) " +
                "值填: repeat_AchievementConfig的ID", color);

            color.normal.textColor = oldcolor;
            #endregion

            #region ---路径---
            GUILayout.Space(20);
            EditorGUILayout.LabelField("---Path---");

            mToolInfo.mProtoUtilityPath = ShowFolderLabel("mProtoUtilityPath :", mToolInfo.mProtoUtilityPath, "ProtoUtility");

            mToolInfo.mExcelFilesPath = ShowFolderLabel("mExcelFilesPath :", mToolInfo.mExcelFilesPath, "");

            mToolInfo.mProtoFilesPath = ShowFolderLabel("mProtoFilesPath :", mToolInfo.mProtoFilesPath, "ProtoFiles");

            mToolInfo.mConfigScriptsSavePath = ShowFolderLabel("mConfigScriptsSavePath :", mToolInfo.mConfigScriptsSavePath, "ProtoScript");

            mToolInfo.mConfigBytesPath = ShowFolderLabel("mConfigBytesPath :", mToolInfo.mConfigBytesPath.Replace(Application.dataPath + "/", ""), "");

            //mToolInfo.mBundleAssetTo = ShowFolderLabel("mBundleAssetTo :", mToolInfo.mBundleAssetTo, "");
            #endregion

            #region ---Build---

            Rect build_4 = new Rect(0f, window.position.height - 48f, 128f, 48f);
            if (GUI.Button(build_4, "ClearData"))
            {
                ClearFiles();
                AssetDatabase.Refresh();
                UnityEngine.Debug.Log("Clear Data Success!");
            }

            Rect build_3 = new Rect(128f, window.position.height - 48f, 128f, 48f);
            if (GUI.Button(build_3, "ClearCache"))
            {
                ExcelSheetBuildTools.ClearCache();
                AssetDatabase.Refresh();
                UnityEngine.Debug.Log("Clear Data Success!");
            }

            Rect build_2 = new Rect(window.position.width - 2 * 128f, window.position.height - 48f, 128f, 48f);
            if (GUI.Button(build_2, "BuildConfigScripts"))
            {
                InitMapping();
                GenerateMessageTable();

                List<string> allMessageName = new List<string>();

                foreach (var message in MessageTable.Values)
                {
                    message.GenerateProtoFile(TypeMapping);
                    message.GenerateExtensionOperation();
                    message.GenerateAssignTools(TypeMapping);
                    allMessageName.Add(message.Name);
                }

                CompileProtoToCsharp();
                GenerateBuildBytesTools(allMessageName);
                AssetDatabase.Refresh();
                UnityEngine.Debug.Log("Create ConfigScripts Success!");
            }

            Rect build_1 = new Rect(window.position.width - 128f, window.position.height - 48f, 128f, 48f);
            if (GUI.Button(build_1, "BuildBytes"))
            {
                InitMapping();
                GenerateMessageTable();
                ExcelSheetBuildTools.Build(MessageTable);
                AssetDatabase.Refresh();
                UnityEngine.Debug.Log("Create ConfigData Success!");
            }
            #endregion

            GUILayout.EndScrollView();
        }

        //-------------------------------------------------------------------------------------------

        /// <summary>
        /// 数据类型
        /// </summary>
        private void InitMapping()
        {
            TypeMapping.Clear();
            TypeMapping.Add("short", "int32");
            TypeMapping.Add("int", "int32");
            TypeMapping.Add("int64", "int64");
            TypeMapping.Add("byte", "bytes");
            TypeMapping.Add("bytes", "bytes");
            TypeMapping.Add("string", "string");
            TypeMapping.Add("bool", "bool");
            TypeMapping.Add("float", "float");
            TypeMapping.Add("double", "double");
        }

        /// <summary>
        /// 收集消息信息
        /// </summary>
        private void GenerateMessageTable()
        {
            MessageTable.Clear();
            string[] excelFilePaths = Directory.GetFiles(mToolInfo.mExcelFilesPath);
            List<string> allClass = new List<string>();

            for (int i = 0; i < excelFilePaths.Length; i++)
            {
                string excelFileName = excelFilePaths[i];

                FileInfo fileInfo = new FileInfo(excelFileName);
                string className = Path.GetFileNameWithoutExtension(excelFilePaths[i]);
                if (className.StartsWith("~$")) continue;

                allClass.Add(className);

                ProtoMessageInfo protoMessageInfo = null;
                if (MessageTable.ContainsKey(className))
                {
                    protoMessageInfo = MessageTable[className];
                    protoMessageInfo.Name = className;
                }
                else
                {
                    protoMessageInfo = new ProtoMessageInfo();
                    protoMessageInfo.Name = className;
                    protoMessageInfo.ExcelFilePath = excelFileName;
                    MessageTable.Add(className, protoMessageInfo);
                }
            }
        }

        /// <summary>
        /// 编译File.Proto文件
        /// </summary>
        private void CompileProtoToCsharp()
        {
            string protocBatPath = Path.Combine(mToolInfo.mProtoUtilityPath, "RunAll.bat");
            string protocBatStringPath = Path.Combine(mToolInfo.mProtoUtilityPath, "RunAllBatString.syntax");

            if (!Directory.Exists(mToolInfo.mConfigScriptsSavePath))
                Directory.CreateDirectory(mToolInfo.mConfigScriptsSavePath);

            using (StreamReader reader = new StreamReader(protocBatStringPath))
            {
                StringBuilder builder = new StringBuilder();
                while (!reader.EndOfStream)
                {
                    string str = reader.ReadLine().Replace("${ProtoFilesPath}", mToolInfo.mProtoFilesPath)
                                       .Replace("${ConfigScriptsPath}", mToolInfo.mConfigScriptsSavePath);
                    builder.AppendLine(str);
                }

                File.WriteAllText(protocBatPath, builder.ToString(), Encoding.GetEncoding(936));

                Process exep = new Process();
                exep.StartInfo.FileName = protocBatPath;
                exep.StartInfo.Arguments = null;
                exep.StartInfo.CreateNoWindow = false;
                exep.StartInfo.UseShellExecute = true;
                exep.Start();
                exep.WaitForExit();//等待外部程序退出后才能往下执行
            }
        }

        /// <summary>
        /// 更新生成bytes生成工具类
        /// </summary>
        /// <param name="names"></param>
        private void GenerateBuildBytesTools(List<string> names)
        {
            string fileDirPath = Path.Combine(mToolInfo.mProtoUtilityPath, "Editor");
            string filePath = Path.Combine(fileDirPath, "ExcelSheetBuildTools.cs");

            StringBuilder builder = new StringBuilder();
            StreamReader reader = new StreamReader(filePath);
            bool delete = false;
            while (!reader.EndOfStream)
            {
                string lineText = reader.ReadLine();

                if (lineText.Contains("${Start}"))
                {
                    builder.AppendLine(lineText);
                    string templeS = lineText.Replace("${Start}", "");
                    for (int i = 0; i < names.Count; i++)
                    {
                        string oneS = templeS.Replace("${ProtoDefine}", mToolInfo.NameSpace)
                                             .Replace("${ClassName}", names[i])
                                             .Replace("//", "");
                        builder.AppendLine(oneS);
                    }
                    delete = true;
                }

                if (lineText.Contains("${End}")) delete = false;

                if (delete) continue;

                builder.AppendLine(lineText);
            }
            reader.Dispose();
            reader.Close();

            SaveFile("ExcelSheetBuildTools.cs", builder.ToString(), fileDirPath);
        }

        //----工具类-----------------------------------------------------------------------------------------

        private string ShowFolderLabel(string title, string content, string defaultFolder)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(title, GUILayout.Width(128));
            EditorGUILayout.LabelField(content);
            if (GUILayout.Button("Change", GUI.skin.label, GUILayout.Width(64), GUILayout.Height(24)))
            {
                string dicPath = EditorUtility.OpenFolderPanel("Select Path", content, defaultFolder);
                if (string.IsNullOrEmpty(dicPath))
                {
                    ShowNotification(new GUIContent("Sure ThePath is Right!"));
                    return content;
                }
                content = dicPath;
            }
            EditorGUILayout.EndHorizontal();
            return content;
        }

        private int ShowIntField(string title, int content, string desc)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(title, GUILayout.Width(128), GUILayout.Height(24));
            int value = EditorGUILayout.IntField(content, GUILayout.Width(128));
            EditorGUILayout.LabelField(desc);
            EditorGUILayout.EndHorizontal();
            return value;
        }

        private string ShowTextField(string title, string content, string desc)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(title, GUILayout.Width(128), GUILayout.Height(24));
            string text = EditorGUILayout.TextField(content, GUILayout.Width(128));
            EditorGUILayout.LabelField(desc);
            EditorGUILayout.EndHorizontal();
            return text;
        }

        private void ShowLabel(string title, string content)
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(title, GUILayout.Width(128), GUILayout.Height(24));
            EditorGUILayout.LabelField(content);
            EditorGUILayout.EndHorizontal();
        }

        /// <summary>
        /// 获取指定路径下面的所有资源文件  
        /// </summary>
        /// <param name="resPath">Full Path :Application.streamingAssetsPath + "/GameConfig" </param>
        /// <returns></returns>
        private static List<UnityEngine.Object> GetAllAssetsAtPath(string resPath)
        {
            List<UnityEngine.Object> tempResult = new List<UnityEngine.Object>();

            if (Directory.Exists(resPath))
            {
                DirectoryInfo direction = new DirectoryInfo(resPath);
                FileInfo[] files = direction.GetFiles("*", SearchOption.AllDirectories);

                resPath = "Assets" + resPath.Replace(Application.dataPath, "");
                UnityEngine.Debug.Log(resPath + " - " + files.Length);

                for (int i = 0; i < files.Length; i++)
                {
                    if (files[i].Name.EndsWith(".meta"))
                    {
                        continue;
                    }
                    tempResult.AddRange(AssetDatabase.LoadAllAssetsAtPath(resPath + "/" + files[i].Name));
                }
            }
            return tempResult;
        }

        /// <summary>
        /// 创建文件
        /// </summary>
        /// <param name="fileName">文件全名</param>
        /// <param name="content">文件内容</param>
        /// <param name="fileSaveDirPath">存储文件的文件夹</param>
        private void SaveFile(string fileName, string content, string fileSaveDirPath)
        {
            if (!Directory.Exists(fileSaveDirPath)) Directory.CreateDirectory(fileSaveDirPath);
            string filePath = Path.Combine(fileSaveDirPath, fileName);
            if (File.Exists(filePath)) File.Delete(filePath);
            File.AppendAllText(filePath, content);
        }

        /// <summary>
        /// 清除数据表及其拓展类
        /// </summary>
        private void ClearFiles()
        {
            DeteleFilesBelowFolder(mToolInfo.mProtoFilesPath, "*.proto");
            //DeteleFilesBelowFolder(mToolInfo.mConfigScriptsSavePath, "*.cs");
            //DeteleFilesBelowFolder(mToolInfo.mConfigExtensionSavePath, "*.cs");
            DeteleFilesBelowFolder(Path.Combine(mToolInfo.mProtoUtilityPath, mToolInfo.AssignDataToolsPath), "*.cs");
            DeteleFilesBelowFolder(Path.Combine(Application.dataPath, mToolInfo.mConfigBytesPath), "*.bytes");
            GenerateBuildBytesTools(new List<string>());
            MessageTable.Clear();
            ExcelSheetBuildTools.MessageTable.Clear();
        }

        private void DeteleFilesBelowFolder(string folderPath, string searchPattern)
        {
            DirectoryInfo dinfo = new DirectoryInfo(folderPath);
            if (!dinfo.Exists) return;
            foreach (var finfo in dinfo.GetFiles(searchPattern))
            {
                finfo.Delete();
            }
        }

        private void LoadUserData()
        {
            string dataPath = System.IO.Path.GetFullPath("./../").Replace("\\", "/");
            dataPath = Path.Combine(dataPath, "Document/数据配置表/ConfigPart.dat");

            if (File.Exists(dataPath))
            {
                BinaryFormatter bf = new BinaryFormatter();
                FileStream file = null;
                try
                {
                    file = File.Open(dataPath, FileMode.Open);
                    if (file.Length > 0) mToolInfo = bf.Deserialize(file) as PathsInfo;
                }
                catch
                {
                    if (mToolInfo == null) mToolInfo = new PathsInfo();
                }

                if (file != null)
                {
                    file.Flush();
                    file.Close();
                    file.Dispose();
                }
            }
            else
            {
                mToolInfo = new PathsInfo();
            }
        }

        private void SaveUserData()
        {
            string dataPath = System.IO.Path.GetFullPath("./../").Replace("\\", "/");

            dataPath = Path.Combine(dataPath, "Document/数据配置表/ConfigPart.dat");

            if (File.Exists(dataPath)) File.Delete(dataPath);

            BinaryFormatter bf = new BinaryFormatter();
            FileStream file = File.Create(dataPath);

            bf.Serialize(file, mToolInfo);
            file.Close();
        }
    }

    [System.Serializable]
    public class PathsInfo
    {
        public string NameSpace = "ProtoDefine";

        public string KeyInExcelToParse = "Sheet1";
        public int NameLineInExcel = 1;
        public int TypeLineInExcel = 2;
        public int DescLineInExcel = 3;
        public int DataLineInExcel = 4;

        private string protoUtilityPath = "";
        public string mProtoUtilityPath
        {
            get
            {
                return GetAbsoluteURL(protoUtilityPath);
            }
            set
            {
                protoUtilityPath = SwitchAbsoluteURL(value);
            }
        }

        private string excelFilesPath = "";
        public string mExcelFilesPath
        {
            get
            {
                return GetAbsoluteURL(excelFilesPath);
            }
            set
            {
                excelFilesPath = SwitchAbsoluteURL(value);
            }
        }

        private string protoFilesPath = "";
        public string mProtoFilesPath
        {
            get
            {
                return GetAbsoluteURL(protoFilesPath);
            }
            set
            {
                protoFilesPath = SwitchAbsoluteURL(value);
            }
        }

        private string configScriptsSavePath = "";
        public string mConfigScriptsSavePath
        {
            get
            {
                return GetAbsoluteURL(configScriptsSavePath);
            }
            set
            {
                configScriptsSavePath = SwitchAbsoluteURL(value);
            }
        }

        private string configBytesPath = "";
        public string mConfigBytesPath
        {
            get
            {
                return GetAbsoluteURL(configBytesPath);
            }
            set
            {
                configBytesPath = SwitchAbsoluteURL(value);
            }
        }

        private string mBundleAssetTo = "";
        public string AssignDataToolsPath = "Editor/AssignDataToolsClass";


        public int RectSize_x = 960;
        public int RectSize_y = 480;

        /// <summary>
        /// 将绝对路径转换到相对路径
        /// </summary>
        private string SwitchAbsoluteURL(string Absurl)
        {
            string relativeUrl = Application.dataPath ;

            System.Uri uri1 = new Uri(Absurl);
            System.Uri uri2 = new Uri(relativeUrl);

            Uri relativeUri = uri2.MakeRelativeUri(uri1);

            return relativeUri.ToString();
        }

        /// <summary>
        /// 相对路径转到绝对路径
        /// </summary>
        private string GetAbsoluteURL(string relativeUrl)
        {
            if (string.IsNullOrEmpty(relativeUrl))
            {
                return "";
            }
            string absURL = Path.GetFullPath(relativeUrl);
            return absURL;
        }
    }
}

